package com.vipboot.platform.system.crud.service.rbac;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.toolkit.MPJWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.vipboot.framework.core.constant.Const;
import com.vipboot.framework.core.constant.TenantConst;
import com.vipboot.framework.core.exception.ServiceException;
import com.vipboot.framework.core.util.AssertCheck;
import com.vipboot.framework.core.util.MapStructUtil;
import com.vipboot.framework.core.util.StringUtil;
import com.vipboot.framework.mybatisplus.core.page.PageData;
import com.vipboot.framework.mybatisplus.core.page.PageQuery;
import com.vipboot.framework.satoken.helper.LoginHelper;
import com.vipboot.platform.system.crud.dao.*;
import com.vipboot.platform.system.crud.service.DataScopeCheckService;
import com.vipboot.platform.system.domain.bo.SysRoleBo;
import com.vipboot.platform.system.domain.bo.SysUserBo;
import com.vipboot.platform.system.domain.entity.*;
import com.vipboot.platform.system.domain.vo.SysRoleVo;
import com.vipboot.platform.system.domain.vo.SysUserVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


@Slf4j
@RequiredArgsConstructor
@Service
public class SysRoleService {
    //    private final DataScopeService dataScopeService;
    private final DataScopeCheckService dataScopeCheckService;
    private final SysRoleDao sysRoleDao;
    private final SysRoleMenuDao sysRoleMenuDao;
    private final SysRoleDeptDao sysRoleDeptDao;
    private final SysUserRoleDao sysUserRoleDao;
    private final SysUserDao sysUserDao;
    private final SysMenuDao sysMenuDao;
    private final SysDeptDao sysDeptDao;


    public PageData<SysRoleVo> page(SysRoleBo bo, PageQuery pageQuery) {
//        Page<SysRoleVo> page = sysRoleDao.pageAs(pageQuery.build(), sysRoleDao.buildQueryWrapper(bo),
//                SysRoleVo.class);
//        return PageData.build(page);
        Page<SysRoleVo> page = sysRoleDao.getBaseMapper().selectPageRoleList(pageQuery.build(),
                sysRoleDao.buildQueryWrapper(bo));
        return PageData.build(page);
    }

    @Transactional
    public boolean save(SysRoleBo bo) {

        this.checkRoleAllowed(bo);
        if (this.checkExistRoleName(bo)) {
            throw new ServiceException("新增角色'" + bo.getRoleName() + "'失败，角色名称已存在");
        }
        if (this.checkExistRoleKey(bo)) {
            throw new ServiceException("新增角色'" + bo.getRoleKey() + "'失败，角色权限已存在");
        }
        SysRole entity = MapStructUtil.convert(bo, SysRole.class);
        AssertCheck.throwIfNull(entity, "添加对象不能为空");

        entity.setDataScope("1");//默认1：全部数据权限
        return sysRoleDao.save(entity);
    }

    @Transactional
    public boolean update(SysRoleBo bo) {

        this.checkRoleAllowed(bo);
        dataScopeCheckService.checkRoleDataScope(bo.getRoleId());

        if (this.checkExistRoleName(bo)) {
            throw new ServiceException("修改角色'" + bo.getRoleName() + "'失败，角色名称已存在");
        }
        if (this.checkExistRoleKey(bo)) {
            throw new ServiceException("修改角色'" + bo.getRoleKey() + "'失败，角色权限已存在");
        }
        if (ObjectUtil.equals(bo.getStatus(), Const.StatusDisable)
                && this.checkRoleHasUserByRoleId(bo.getRoleId())) {
            throw new ServiceException("角色已分配，不能停用!");
        }
//        if (updated) {
//            roleService.cleanOnlineUserByRole(roleBo.getRoleId());
//            return R.ok();
//        }
        SysRole entity = MapStructUtil.convert(bo, SysRole.class);
        AssertCheck.throwIfNull(entity, "修改对象不能为空");

        return sysRoleDao.updateById(entity);
    }


    public boolean removeByIds(List<Long> ids) {
        ids.forEach(id -> {
            SysRoleBo roleBo = new SysRoleBo();
            roleBo.setRoleId(id);
            this.checkRoleAllowed(roleBo);
            dataScopeCheckService.checkRoleDataScope(id);

            SysRole entity = sysRoleDao.getById(id);
            if (ObjectUtil.equals(entity.getStatus(), Const.StatusNormal)
                    && this.checkRoleHasUserByRoleId(entity.getRoleId())) {
                throw new ServiceException(String.format("%1$s 角色已分配,不能删除!", entity.getRoleName()));
            }
        });
        // 删除角色与菜单关联
        this.removeRoleMenuByRoleIds(ids);
        // 删除角色与部门关联
        this.removeRoleDeptByRoleIds(ids);

        //逻辑删除：update sys_role set del_flag = '1' where role_id in
        return sysRoleDao.removeByIds(ids);
    }


    /**
     * 查询当前角色已分配的用户列表 / 可条件搜索
     *
     * @param bo 用户信息
     * @return 用户信息集合信息
     */
    public PageData<SysUserVo> pageAllocatedUser(SysUserBo bo, PageQuery pageQuery) {
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.delete_flag", Const.StatusNormal)
                .eq(ObjectUtil.isNotNull(bo.getRoleId()), "r.role_id", bo.getRoleId())
                .like(StringUtil.isNotBlank(bo.getUserName()), "u.user_name", bo.getUserName())
                .eq(StringUtil.isNotBlank(bo.getStatus()), "u.status", bo.getStatus())
                .like(StringUtil.isNotBlank(bo.getPhoneNumber()), "u.phone_number", bo.getPhoneNumber())
                .orderByAsc("u.user_id");
        Page<SysUserVo> page = sysUserDao.getBaseMapper().selectAllocatedList(pageQuery.build(), wrapper);
        return PageData.build(page);
    }

    /**
     * 查询当前角色未分配的用户列表 (用户列表包括其他角色已分配的用户) / 可条件搜索
     *
     * @param bo 用户信息
     * @return 用户信息集合信息
     */
    public PageData<SysUserVo> pageUnAllocatedUser(SysUserBo bo, PageQuery pageQuery) {

        MPJLambdaWrapper<SysUser> queryWrapper = MPJWrappers.lambdaJoin();
        queryWrapper.select(SysUser::getUserId)
                .innerJoin(SysUserRole.class, on -> {
                    on.eq(SysUserRole::getUserId, SysUser::getUserId);
                    on.eq(SysUserRole::getRoleId, bo.getRoleId());
                    return on;
                });
        List<Long> userIds = sysUserDao.selectJoinList(Long.class, queryWrapper);
//        List<Long> userIds = userRoleMapper.selectUserIdsByRoleId(bo.getRoleId());

        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper.eq("u.delete_flag", Const.StatusNormal)
                .and(w -> w.ne("r.role_id", bo.getRoleId()).or().isNull("r.role_id"))
                .notIn(CollUtil.isNotEmpty(userIds), "u.user_id", userIds)
                .like(StringUtil.isNotBlank(bo.getUserName()), "u.user_name", bo.getUserName())
                .like(StringUtil.isNotBlank(bo.getPhoneNumber()), "u.phone_number", bo.getPhoneNumber())
                .orderByAsc("u.user_id");
        Page<SysUserVo> page = sysUserDao.getBaseMapper().selectUnallocatedList(pageQuery.build(), wrapper);

        page.getRecords().forEach(user -> {
            MPJLambdaWrapper<SysRole> joinWrapper = JoinWrappers.lambda();
            joinWrapper.leftJoin(SysUserRole.class, SysUserRole::getRoleId, SysRole::getRoleId)
                    .eq(SysUserRole::getUserId, user.getUserId());
            List<SysRoleVo> roles = sysRoleDao.selectJoinList(SysRoleVo.class, joinWrapper);
//            user.setRoles(roles);
            user.setRoleNameList(StringUtil.join(roles.stream()
                    .map(SysRoleVo::getRoleName).toList(), " "));
        });
        return PageData.build(page);
    }


    /**
     * 取消授权用户:删除用户和角色关联信息
     * delete from sys_user_role where user_id=#{userId} and role_id=#{roleId}
     *
     * @param roleId 角色ID
     * @param userId 需要删除的用户数据 ID
     * @return 结果:true 删除成功，false 删除失败
     */
    public boolean cancelAuthUser(Long roleId, Long userId) {
        LambdaQueryWrapper<SysUserRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUserRole::getRoleId, roleId)
                .eq(SysUserRole::getUserId, userId);
        return sysUserRoleDao.remove(lqw);
    }

    /**
     * 批量取消授权用户:删除用户和角色关联信息
     * delete from sys_user_role where role_id=#{roleId} and user_id in
     *
     * @param roleId  角色ID
     * @param userIds 需要删除的用户数据ID
     * @return 结果:true 删除成功，false 删除失败
     */
    public boolean cancelAllAuthUser(Long roleId, Long[] userIds) {
        LambdaQueryWrapper<SysUserRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysUserRole::getRoleId, roleId)
                .in(SysUserRole::getUserId, List.of(userIds));
        return sysUserRoleDao.remove(lqw);
    }

    /**
     * 批量授权用户:新增用户和角色关联信息
     * insert into sys_user_role(user_id, role_id) values
     *
     * @param roleId  角色ID
     * @param userIds 需要新增的用户数据ID
     * @return 结果:true 保存成功，false 保存失败
     */
    public boolean authUser(Long roleId, Long[] userIds) {
//        dataScopeCheckService.checkRoleDataScope(roleId);
        boolean inserted = true;
        // 新增用户与角色管理
        List<SysUserRole> list = new ArrayList<>();
        Arrays.stream(userIds)
                .forEach(userId -> {
                    SysUserRole ur = new SysUserRole();
                    ur.setUserId(userId);
                    ur.setRoleId(roleId);
                    list.add(ur);
                });
        if (CollUtil.isNotEmpty(list)) {
            //批量插入
            inserted = sysUserRoleDao.saveBatch(list, 100);
        }
//        if (inserted){
//            cleanOnlineUserByRole(roleId);
//        }
        return inserted;
    }


    /**
     * 分配角色菜单权限:新增角色和菜单关联信息
     *
     * @param bo 角色信息
     * @return 结果
     */
    @Transactional
    public boolean authMenu(SysRoleBo bo) {
        SysRole entity = MapStructUtil.convert(bo, SysRole.class);
        AssertCheck.throwIfNull(entity.getRoleId(), "修改对象不能为空");

        this.checkRoleAllowed(bo);
        dataScopeCheckService.checkRoleDataScope(bo.getRoleId());
        // 修改角色信息
//        sysRoleDao.updateById(entity);

        // 删除角色与菜单关联
        this.removeRoleMenuByRoleIds(List.of(bo.getRoleId()));
        // 新增角色菜单信息（数据权限）
        return this.saveRoleMenu(bo);
    }


    /**
     * 分配角色数据权限:新增角色和部门关联信息
     *
     * @param bo 角色信息
     * @return 结果
     */
    @Transactional
    public boolean authDataScope(SysRoleBo bo) {
        SysRole entity = MapStructUtil.convert(bo, SysRole.class);
        AssertCheck.throwIfNull(entity, "修改对象不能为空");

        this.checkRoleAllowed(bo);
        dataScopeCheckService.checkRoleDataScope(bo.getRoleId());

        // 修改角色信息
        sysRoleDao.updateById(entity);
        // 删除角色与部门关联
        this.removeRoleDeptByRoleIds(List.of(bo.getRoleId()));
        // 新增角色和部门信息（数据权限）
        return this.saveRoleDept(bo);
    }

    /**
     * 根据角色ID查询菜单树信息(已分配)
     *
     * @param roleId 角色ID
     * @return 选中菜单列表
     */
    public List<Long> listAllocatedMenuIdByRoleId(Long roleId) {
        LambdaQueryWrapper<SysRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysRole::getRoleId, roleId);
        SysRole role = sysRoleDao.getOne(lqw);

        /*select m.menu_id
        from sys_menu m
        left join sys_role_menu rm on m.menu_id = rm.menu_id
        where rm.role_id = #{roleId}
            <if test="menuCheckStrictly">
            and m.menu_id not in (select m.parent_id from sys_menu m inner join sys_role_menu rm on m.menu_id = rm.menu_id and rm.role_id = #{roleId})
            </if>
        order by m.parent_id, m.order_num*/
        MPJLambdaWrapper<SysMenu> joinWrapper = JoinWrappers.lambda();
        joinWrapper.select(SysMenu::getMenuId)
                .leftJoin(SysRoleMenu.class, SysRoleMenu::getMenuId, SysMenu::getMenuId)
                .eq(SysRoleMenu::getRoleId, roleId);

        if (role.getMenuCheckStrictly()) {
            MPJLambdaWrapper<SysMenu> subJoinWrapper = JoinWrappers.lambda();
            subJoinWrapper.select(SysMenu::getParentId)
                    .innerJoin(SysRoleMenu.class, SysRoleMenu::getMenuId, SysMenu::getMenuId)
                    .eq(SysRoleMenu::getRoleId, roleId);
            List<Long> menuIds = sysMenuDao.selectJoinList(Long.class, subJoinWrapper);
            joinWrapper.notIn(ObjectUtil.isNotEmpty(menuIds), SysMenu::getMenuId, menuIds);
        }
        joinWrapper.orderByAsc(SysMenu::getParentId);
        joinWrapper.orderByAsc(SysMenu::getSortNum);
        return sysMenuDao.selectJoinList(Long.class, joinWrapper);
    }


    /**
     * 根据角色ID查询部门树信息(已分配)
     *
     * @param roleId 角色ID
     * @return 选中部门列表
     */
    public List<Long> listAllocatedDeptIdByRoleId(Long roleId) {

        LambdaQueryWrapper<SysRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysRole::getRoleId, roleId);
        SysRole role = sysRoleDao.getOne(lqw);

        /*select d.dept_id
        from sys_dept d
        left join sys_role_dept rd on d.dept_id = rd.dept_id
        where rd.role_id = #{roleId}
            <if test="deptCheckStrictly">
            and d.dept_id not in (select d.parent_id from sys_dept d inner join sys_role_dept rd on d.dept_id = rd.dept_id and rd.role_id = #{roleId})
            </if>
        order by d.parent_id, d.order_num*/
        MPJLambdaWrapper<SysDept> joinWrapper = JoinWrappers.lambda();
        joinWrapper.select(SysDept::getDeptId)
                .leftJoin(SysRoleDept.class, SysRoleDept::getDeptId, SysDept::getDeptId)
                .eq(SysRoleDept::getRoleId, roleId);

        //部门树选择项是否关联显示
        if (ObjectUtil.isNotNull(role.getDeptCheckStrictly()) && role.getDeptCheckStrictly()) {
            MPJLambdaWrapper<SysDept> subJoinWrapper = JoinWrappers.lambda();
            subJoinWrapper.select(SysDept::getParentId)
                    .innerJoin(SysRoleDept.class, SysRoleDept::getDeptId, SysDept::getDeptId)
                    .eq(SysRoleDept::getRoleId, roleId);
            List<Long> deptIds = sysDeptDao.selectJoinList(Long.class, subJoinWrapper);
            joinWrapper.notIn(ObjectUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds);
        }
        joinWrapper.orderByAsc(SysDept::getParentId);
        joinWrapper.orderByAsc(SysDept::getSortNum);
        return sysDeptDao.selectJoinList(Long.class, joinWrapper);
    }


    /**
     * 获取角色选择框列表
     **/
    public PageData<SysRoleVo> pageOptionSelect(PageQuery pageQuery) {
        QueryWrapper<SysRole> wrapper = Wrappers.query();
        wrapper.eq("r.delete_flag", Const.StatusNormal)
                .eq("r.status", Const.StatusNormal);
        Page<SysRoleVo> page = sysRoleDao.getBaseMapper().selectPageRoleList(pageQuery.build(),
                wrapper);
        return PageData.build(page);
    }

    /**
     * 校验角色名称是否唯一
     */
    private boolean checkExistRoleName(SysRoleBo bo) {
        LambdaQueryWrapper<SysRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysRole::getRoleName, bo.getRoleName());
        lqw.ne(ObjectUtil.isNotNull(bo.getRoleId()), SysRole::getRoleId, bo.getRoleId());
        return sysRoleDao.exists(lqw);
    }

    /**
     * 校验角色权限是否唯一
     */
    private boolean checkExistRoleKey(SysRoleBo bo) {
        LambdaQueryWrapper<SysRole> lqw = Wrappers.lambdaQuery();
        lqw.eq(SysRole::getRoleKey, bo.getRoleKey());
        lqw.ne(ObjectUtil.isNotNull(bo.getRoleId()), SysRole::getRoleId, bo.getRoleId());
        return sysRoleDao.exists(lqw);
    }

    /**
     * 校验角色是否存在用户
     *
     * @param roleId 角色ID
     * @return 结果
     */
    private boolean checkRoleHasUserByRoleId(Long roleId) {
        LambdaQueryWrapper<SysUserRole> lqw = Wrappers.lambdaQuery();
        lqw.select(SysUserRole::getUserId);
        lqw.eq(SysUserRole::getRoleId, roleId);
        return sysUserRoleDao.exists(lqw);
    }

    /**
     * 校验角色是否允许操作
     *
     * @param bo 角色信息
     */
    private void checkRoleAllowed(SysRoleBo bo) {
        if (ObjectUtil.isNotNull(bo.getRoleId()) && LoginHelper.isSuperAdmin(bo.getRoleId())) {
            throw new ServiceException("不允许操作超级管理员角色");
        }
        String[] keys = new String[]{TenantConst.SuperAdminRoleKey, TenantConst.TenantAdminRoleKey};
        // 新增:不允许使用 管理员标识符
        if (ObjectUtil.isNull(bo.getRoleId())
                && StrUtil.equalsAny(bo.getRoleKey(), keys)) {
            throw new ServiceException("不允许使用系统内置管理员角色标识符!");
        }
        // 修改:不允许修改 管理员标识符
//        if (ObjectUtil.isNotNull(bo.getRoleId())) {
//            SysRole sysRole = sysRoleDao.getById(bo.getRoleId());
//            // 如果标识符不相等 判断为修改了管理员标识符
//            if (ObjectUtil.notEqual(sysRole.getRoleKey(), bo.getRoleKey())) {
//                if (StrUtil.equalsAny(sysRole.getRoleKey(), keys)) {
//                    throw new ServiceException("不允许修改系统内置管理员角色标识符!");
//                } else if (StrUtil.equalsAny(bo.getRoleKey(), keys)) {
//                    throw new ServiceException("不允许使用系统内置管理员角色标识符!");
//                }
//            }
//        }
    }

    /**
     * 新增角色菜单信息 (菜单权限)
     *
     * @param bo 角色对象
     * @return true 保存成功，false 保存失败
     */
    private boolean saveRoleMenu(SysRoleBo bo) {
        boolean inserted = true;
        // 新增用户与角色管理
        List<SysRoleMenu> list = new ArrayList<>();
        Optional.ofNullable(bo.getMenuIds()).orElse(List.of())
                .forEach(menuId -> {
                    SysRoleMenu rm = new SysRoleMenu();
                    rm.setRoleId(bo.getRoleId());
                    rm.setMenuId(menuId);
                    list.add(rm);
                });
        if (CollUtil.isNotEmpty(list)) {
            inserted = sysRoleMenuDao.saveBatch(list);
        }
        return inserted;
    }

    /**
     * 新增角色部门信息(数据权限)
     *
     * @param bo 角色对象
     */
    private boolean saveRoleDept(SysRoleBo bo) {
        boolean inserted = true;
        // 新增角色与部门（数据权限）管理
        List<SysRoleDept> list = new ArrayList<>();
        Optional.ofNullable(bo.getDeptIds()).orElse(List.of())
                .forEach(deptId -> {
                    SysRoleDept rd = new SysRoleDept();
                    rd.setRoleId(bo.getRoleId());
                    rd.setDeptId(deptId);
                    list.add(rd);
                });
        if (CollUtil.isNotEmpty(list)) {
            inserted = sysRoleDeptDao.saveBatch(list);
        }
        return inserted;
    }

    /**
     * 批量删除角色菜单关联信息
     * delete from sys_role_menu where role_id in
     *
     * @param roleIds 需要删除的数据ID
     */
    private void removeRoleMenuByRoleIds(List<Long> roleIds) {
        LambdaQueryWrapper<SysRoleMenu> lqw = Wrappers.lambdaQuery();
        lqw.in(SysRoleMenu::getRoleId, roleIds);
        sysRoleMenuDao.remove(lqw);
    }


    /**
     * 批量删除角色部门关联信息
     * delete from sys_role_dept where role_id in
     *
     * @param roleIds 需要删除的数据ID
     */
    private void removeRoleDeptByRoleIds(List<Long> roleIds) {
        LambdaQueryWrapper<SysRoleDept> lqw = Wrappers.lambdaQuery();
        lqw.in(SysRoleDept::getRoleId, roleIds);
        sysRoleDeptDao.remove(lqw);
    }
}
